package org.wat.wcy.isi.mmazur.bp.io.xpdl.processors;

import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

import org.apache.commons.lang.StringUtils;
import org.wat.wcy.isi.mmazur.bp.io.model.GraphicsInfo;
import org.wat.wcy.isi.mmazur.bp.io.model.IdDesc;
import org.wat.wcy.isi.mmazur.bp.io.model.Loop;
import org.wat.wcy.isi.mmazur.bp.io.model.Node;
import org.wat.wcy.isi.mmazur.bp.io.model.Transition;
import org.wat.wcy.isi.mmazur.bp.io.model.TransitionCondition;
import org.wat.wcy.isi.mmazur.bp.io.model.enums.ConditionType;
import org.wat.wcy.isi.mmazur.bp.io.model.enums.LoopType;
import org.wat.wcy.isi.mmazur.bp.io.model.enums.MILoopOrderingType;
import org.wat.wcy.isi.mmazur.bp.io.model.enums.NodeType;
import org.wat.wcy.isi.mmazur.bp.io.model.enums.TestTimeType;
import org.wat.wcy.isi.mmazur.bp.io.model.enums.TransitionType;
import org.wat.wcy.isi.mmazur.bp.io.model.temporary.Route;
import org.wat.wcy.isi.mmazur.bp.io.xpdl.IXMLProcessor;
import org.xml.sax.Attributes;

public class WorkflowProcessProcessor implements IXMLProcessor {

	private Map<String, Node> nodes;
	private Map<Route, Transition> transitions;
	private Node currentNode;
	private Stack<Node> parentProcessStack;

	private Route currentRoute;
	private Transition currentTransition;
	
	
	public WorkflowProcessProcessor() {
		nodes = new HashMap<String, Node>();
		transitions = new HashMap<Route, Transition>();
		parentProcessStack = new Stack<Node>();
	}
		
	@Override
	public void startElement(String name, final Attributes attr) {
		if (StringUtils.equals(name, "/Package/WorkflowProcesses/WorkflowProcess")) {
			currentNode = new Node();
			currentNode.setType(NodeType.PROCESS);
			//String id = attr.getValue("Id");
			currentNode.setId(new IdDesc());
//			currentNode.setName(attr.getValue("Name"));
//			nodes.put(id, currentNode);
			parentProcessStack.push(currentNode);
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Activities/Activity)|(ActivitySets/ActivitySet/Activities/Activity))")) {
			currentNode = new Node();
			String transaction = attr.getValue("IsATransaction");
			currentNode.setTransaction(StringUtils.equalsIgnoreCase(transaction, "true"));
			currentNode.setType(NodeType.TASK);
			currentNode.setId(new IdDesc(attr.getValue("Id")));
			currentNode.setName(attr.getValue("Name"));
			Node parent = parentProcessStack.peek();
			Node parentNode = new Node();
			parentNode.setId(new IdDesc(parent.getId().toString()));
			currentNode.setParent(parentNode);
		} else if (StringUtils.equalsIgnoreCase(name, "/Package/WorkflowProcesses/WorkflowProcess/ActivitySets/ActivitySet")) {
			String id = attr.getValue("Id");
			String taskName = attr.getValue("Name");
			currentNode = new Node();
			currentNode.setId(new IdDesc(id));
			currentNode.setName(taskName);
			Node parent = parentProcessStack.peek();
			Node parentNode = new Node();
			parentNode.setId(new IdDesc(parent.getId().toString()));
			currentNode.setParent(parentNode);
			parentProcessStack.push(currentNode);
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Activities/Activity)|(ActivitySets/ActivitySet/Activities/Activity))/BlockActivity")) {
			currentNode.setType(NodeType.SUBTASK);
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Activities/Activity)|(ActivitySets/ActivitySet/Activities/Activity))/Event/StartEvent")) {
			String result = attr.getValue("Trigger");
			currentNode.setType(NodeType.START_NONE);
			if (StringUtils.isNotEmpty(result)) {
				currentNode.setType(NodeType.valueOf("START_" +result.toUpperCase()));
			}
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Activities/Activity)|(ActivitySets/ActivitySet/Activities/Activity))/Event/EndEvent")) {
			String result = attr.getValue("Result");
			currentNode.setType(NodeType.END_NONE);
			if (StringUtils.isNotEmpty(result)) {
				currentNode.setType(NodeType.valueOf("END_" + result.toUpperCase()));
			}
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Activities/Activity)|(ActivitySets/ActivitySet/Activities/Activity))/Event/IntermediateEvent")) {
			String intermediateType = attr.getValue("Trigger");
			if (StringUtils.isNotEmpty(intermediateType)) {
				currentNode.setType(NodeType.valueOf(String.format("INTERMEDIATE_%s", intermediateType.toUpperCase())));
			}
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/Activities/Activity/Implementation/Task/Task[a-zA-Z]{1,}")) {
			String tmp = name.substring(name.lastIndexOf("/")+1,name.length());
			currentNode.setType(NodeType.valueOf(tmp.toUpperCase()));
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Activities/Activity)|(ActivitySets/ActivitySet/Activities/Activity))/Loop")) {
			String loopType = attr.getValue("LoopType");
			if (StringUtils.isNotEmpty(loopType)) {
				Loop loop = new Loop();
				loop.setType(LoopType.valueOf(loopType.toUpperCase()));
				currentNode.setLoop(loop);
			}
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Activities/Activity)|(ActivitySets/ActivitySet/Activities/Activity))/Loop/LoopStandard")) {
			int loopCounter = Integer.parseInt(attr.getValue("LoopCounter"));
			int loopMax = Integer.parseInt(attr.getValue("LoopMaximum"));
			String testTime = attr.getValue("TestTime");
			TestTimeType ttt = TestTimeType.NONE;
			if (StringUtils.isNotEmpty(testTime)) {
				ttt = TestTimeType.valueOf(testTime.toUpperCase());
			}
			Loop loop = currentNode.getLoop();
			loop.setLoopCounter(loopCounter);
			loop.setLoopMax(loopMax);
			loop.setTestTime(ttt);
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Activities/Activity)|(ActivitySets/ActivitySet/Activities/Activity))/Loop/LoopMultiInstance")) {
			int loopCounter = Integer.parseInt(attr.getValue("LoopCounter"));
			String miOrdering = attr.getValue("MI_Ordering");
			MILoopOrderingType miot = MILoopOrderingType.NONE;
			if (StringUtils.isNotEmpty(miOrdering)) {
				miot = MILoopOrderingType.valueOf(miOrdering.toUpperCase());
			}
			Loop loop = currentNode.getLoop();
			loop.setMiLoopOrdering(miot);
			loop.setLoopCounter(loopCounter);
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Activities/Activity)|(ActivitySets/ActivitySet/Activities/Activity))/Route")) {
			String gatewayType = attr.getValue("GatewayType");
			if (StringUtils.isNotEmpty(gatewayType)) {
				currentNode.setType(NodeType.valueOf(String.format("GATEWAY_%s", gatewayType.toUpperCase())));
			} else {
				currentNode.setType(NodeType.GATEWAY);
			}
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Transitions/Transition)|(ActivitySets/ActivitySet/Transitions/Transition))")) {
			currentTransition = new Transition();
			String from = attr.getValue("From");
			String to = attr.getValue("To");
			String id = attr.getValue("Id");
			String transitionName = attr.getValue("Name");
			currentTransition.setType(TransitionType.SEQUENCE);
			currentTransition.setId(new IdDesc(id));
			currentTransition.setName(transitionName);
			currentRoute = new Route(from, to);
			currentTransition.setCondition(new TransitionCondition());
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Transitions/Transition)|(ActivitySets/ActivitySet/Transitions/Transition))/Type")) {
			String conditionType = attr.getValue("Type");
			if (StringUtils.isNotEmpty(conditionType)) {
				ConditionType type = ConditionType.valueOf(conditionType);
				currentTransition.setCondition(new TransitionCondition(type));
			}
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Activities)|(ActivitySets/ActivitySet))/Activity/NodeGraphicsInfos/NodeGraphicsInfo")) {
			int height = Integer.parseInt(attr.getValue("Height"));
			int width = Integer.parseInt(attr.getValue("Width"));
			currentNode.setGraphicsInfo(new GraphicsInfo(0,0,width, height));
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Activities)|(ActivitySets/ActivitySet))/Activity/OutputSets/OutputSet")) {
			String artifactId = attr.getValue("ArtifactId");
			Transition trans = new Transition();
			trans.setType(TransitionType.ASSOCIATION);
			Route route = new Route(currentNode.getId().toString(), artifactId);
			transitions.put(route, trans);
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Activities)|(ActivitySets/ActivitySet))/Activity/NodeGraphicsInfos/NodeGraphicsInfo/Coordinates")) {
			int x = Integer.parseInt(attr.getValue("XCoordinate"));
			int y = Integer.parseInt(attr.getValue("YCoordinate"));
			currentNode.getGraphicsInfo().setX(x);
			currentNode.getGraphicsInfo().setY(y);
		} else {
			for (int i = 0; i < attr.getLength(); i++) { 
				String qName = attr.getQName(i);
				String value = attr.getValue(i);
				currentNode.getAdditionalTags().put(String.format("%s@%s", getShortName(name), qName), value);
			}
		}
	}

	@Override
	public void endElement(String name, String characters) {
		String value = characters != null ? characters : "";
		if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Activities/Activity)|(ActivitySets/ActivitySet/Activities/Activity))")) {
			nodes.put(currentNode.getId().toString(), currentNode);
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Transitions/Transition)|(ActivitySets/ActivitySet/Transitions/Transition))")) {
			transitions.put(currentRoute, currentTransition);
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Transitions/Transition)|(ActivitySets/ActivitySet/Transitions/Transition))/Condition/Expression")) {
			if (StringUtils.isNotEmpty(characters)) {
				currentTransition.getCondition().setExpression(characters);
			}
		} else if (StringUtils.equalsIgnoreCase(name, "/Package/WorkflowProcesses/WorkflowProcess/ActivitySets/ActivitySet") ||
				   StringUtils.equalsIgnoreCase(name, "/Package/WorkflowProcesses/WorkflowProcess")) {
			parentProcessStack.pop();
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Activities/Activity)|(ActivitySets/ActivitySet/Activities/Activity))/Loop/LoopMultiInstance")) {
			currentNode.getLoop().setCondition(value);
		} else if (name.matches("/Package/WorkflowProcesses/WorkflowProcess/((Activities/Activity)|(ActivitySets/ActivitySet/Activities/Activity))/Loop/LoopStandard")) {
			currentNode.getLoop().setCondition(value);
		} else {
			currentNode.getAdditionalTags().put(getShortName(name), value);
		}
	}
	
	private String getShortName(final String name) {
		final String word = "/Package/WorkflowProcesses/WorkflowProcess";
		return name.replace(word, "");
	}

	public Map<String, Node> getNodes() {
		return nodes;
	}

	public Map<Route, Transition> getTransitions() {
		return transitions;
	}

	@Override
	public Map<String, Node> getPools() {
		return null;
	}
}
